﻿using System;

using System.Collections.Generic;
using System.Text;
using Silvermoon.Images;
using Silvermoon.Visuals;
using Silvermoon.Utils;
using System.Drawing;
using Silvermoon.OpenGL;
using Silvermoon.Core;
using Silvermoon.Controls.Classes;
using System.IO;
using System.Reflection;
using Silvermoon.Controls.Sys;

namespace Silvermoon.Controls
{
    /// <summary>
    /// Control that provides text input for other controls.
    /// </summary>
    public class KeyboardPanel : Control, IMouseEventBehavior, IScrollEventBehavior
    {
        #region fields
        public const int KeyboardHeight = 224;
        const int keyHeight = 48;
        const int keyWidth = 48;

        private string[][] keys;
        private InternalToolTip toolTip;
        private KeyButton selectedButton;
        private KeyButton shiftKey;
        private KeyButton backSpaceKey;
        private KeyButton returnKey;
        private KeyButton ctrlKey;
        private KeyButton spaceKey;

        private BackgroundVisual background;
        private List<Visual> visuals;
        private List<Control> buttons;
        private Texture glowTexture;
        private int shiftState = 0;
        private Color color;

        readonly string[][] keyboardKeys = 
            {
                new string[]{"qQ1!","wW2\"","eE3§","rR4$","tT5%","yY6&","uU7/","iI8(","oO9)","pP0°"},
                new string[]{"aA.!","sS,?","dD<:","fF>;","gG+_","hH-'","jJ*|","kK/{","lL^}"},
                new string[]{"zZ@³","xX#€","cC(ä","vV)ö","bB[ü","nN]ß","mM=~"}
            };

        #endregion
        #region ctor/dtor

        public KeyboardPanel()
            : base()
        {
            visuals = new List<Visual>();
            buttons = new List<Control>();
            Texture texture = new Texture();
            Stream s = Assembly.GetExecutingAssembly().GetManifestResourceStream("Silvermoon.Images.GlowButton.png");
            texture.LoadFromStream(s);
            glowTexture = texture;

            color = SysColors.BackgroundColor;
            background = new BackgroundVisual { Colors = ColorUtil.CreatePlasticColors(color), Texture = SysImages.Rectangle };
            visuals.Add(background);
            Size = new Size(480, KeyboardHeight);

            //string[][] germanKeys = 
            //{
            //    new string[]{"qQ1!","wW2\"","eE3§","rR4$","tT5%","zZ6&","uU7/","iI8(","oO9)","pP0="},
            //    new string[]{"aA+\\","sS-~","dD*'","fF/<","gG.>","hH,>","jJ#^","kK%°","lL&²"},
            //    new string[]{"yY@³","xX€","cC;","vV:","bB_","nN!","mM?"}
            //};


            BuildKeys(keyboardKeys);
            Visuals = visuals;

            int size = 100;
            toolTip = new InternalToolTip
            {
                Font = new FontSprite(new Font(FontFamily.GenericSansSerif, 12f, FontStyle.Bold)),
                ScaleFactor = 2f,
                LineAlignment = Alignment.Center,
                Alignment = Alignment.Center,
                TextColor = Color.Black,
                Bounds = new Rectangle((Width - size) / 2, (Screen.Current.Height - size) / 3, size, size)
            };
            Visible = false;
        }

        public Color Color
        {
            get { return color; }
            set
            {
                if (color != value)
                {
                    color = value;
                    Invalidate(InvalidationFlag.Color);
                }
            }
        }

        /// <summary>
        /// Gets or sets the matrix for the key characters.
        /// </summary>
        public string[][] Keys
        {
            get { return keys != null ? keys : keyboardKeys; }
            set
            {
                if (keys != value)
                {
                    keys = value;
                    BuildKeys(keys);
                }
            }
        }


        public override void Dispose()
        {
            toolTip.Dispose();
            base.Dispose();
        }

        #endregion
        #region Properties

        public int ShiftState
        {
            get { return shiftState; }
            set
            {
                shiftState = value;
                SetShift();
            }
        }

        private KeyButton SelectedButton
        {
            get { return selectedButton; }
            set
            {
                if (selectedButton != value)
                {
                    if (selectedButton != null) selectedButton.IsSelected = false;
                    selectedButton = value;
                    if (selectedButton != null) selectedButton.IsSelected = true;
                    SetToolTip(selectedButton);
                }
            }
        }

        #endregion
        #region Methods

        private void SetToolTip(KeyButton button)
        {
            if (button != null && !button.IsSystem)
            {
                Screen.Current.Attach(toolTip);
                string txt = button.GetKey();
                toolTip.Text = txt;
            }
            else
            {
                Screen.Current.Detach(toolTip);
            }
        }

        protected internal override IList<Control> SubControls { get { return buttons; } }

        private void BuildKeys(string[][] keyboardKeys)
        {
            buttons.Clear();
            int y = 0;
            foreach (string[] line in keyboardKeys)
            {
                BuildLine(y, line);
                y += keyHeight;
            }

            const int w1 = 74;
            shiftKey = CreateButton(0, keyHeight * 2, w1, "shft", true);
            backSpaceKey = CreateButton(Width - w1, keyHeight * 2, w1, "bsp", true);
            spaceKey = CreateButton(Width / 4, keyHeight * 3, Width / 2, "", false);
            ctrlKey = CreateButton(0, keyHeight * 3, Width / 4, ".?123", true);
            returnKey = CreateButton(Width - Width / 4, keyHeight * 3, Width / 4, "return", true);
            returnKey.Character = "\n";
            backSpaceKey.Character = "\b";

            Flags = ControlFlags.IgnoreChildClipping;
        }

        private void BuildLine(int y, string[] line)
        {
            int x = (Width - (line.Length * keyWidth)) / 2;
            foreach (string text in line)
            {
                CreateButton(x, y, keyWidth, text, false);
                x += keyWidth;
            }

        }

        private KeyButton CreateButton(int x, int y, int w, string text, bool isSystem)
        {
            y += 8;
            KeyButton btn = new KeyButton { Bounds = new Rectangle(x, y, w, keyHeight), IsSystem = isSystem, Characters = text };
            buttons.Add(btn);
            return btn;
        }

        protected override void OnLayout()
        {
            if (CheckFlag(InvalidationFlag.Color))
            {
                background.Colors = ColorUtil.CreateGradientColor(color);
            }
            if (CheckFlag(InvalidationFlag.Size | InvalidationFlag.Margin))
            {
                background.Bounds = Control.ScaledRect(-4, 0, Width + 8, Height);
            }
            base.OnLayout();
        }

        private void DetermineKey(Point point)
        {
            KeyButton selectedButton = null;
            foreach (var btn in buttons)
            {
                if (btn.Bounds.Contains(point))
                {
                    selectedButton = (KeyButton)btn;
                    break;
                }
            }
            SelectedButton = selectedButton;
        }

        protected override void OnVisibleChanged()
        {
            if (!Visible) ShiftState = 0;
            base.OnVisibleChanged();
        }

        private void SendKey()
        {
            KeyButton btn = this.SelectedButton;
            if (btn != null)
            {
                if (btn == spaceKey)
                {
                    Screen.Current.MessageServer.KeyPress(' ');
                }
                else if (btn == shiftKey)
                {
                    ShiftState = ShiftState != 0 ? 0 : 1;
                }
                else if (btn == ctrlKey)
                {
                    switch (ShiftState)
                    {
                        case 1:
                        case 0: ShiftState = 2; break;
                        case 2: ShiftState = 3; break;
                        default: ShiftState = 0; break;

                    }
                }
                else if (btn != null)
                {
                    btn.KeyPress();
                    if (ShiftState == 1) ShiftState = 0;
                    SetShift();
                }
            }
        }

        private void SetShift()
        {
            for (int i = 0; i < buttons.Count; i++)
            {
                ((KeyButton)buttons[i]).ShiftState = shiftState;
            }
        }

        #endregion
        #region IMouseEventBehavior Members

        public void OnMouseDown(MouseDownEventArgs e)
        {
            if (!e.Handled)
            {
                e.Handled = true;
                DetermineKey(e.ScreenToClient(e.Position));
            }
        }

        public void OnMouseUp(MouseUpEventArgs e)
        {
            if (!e.Handled)
            {
                SendKey();
                SelectedButton = null;
                e.AllowDoubleClick = false;
                e.Handled = true;
            }
        }

        public void OnMouseClick(MouseClickEventArgs e)
        {
            if (!e.Handled)
            {
                e.Handled = true;
                SelectedButton = null;
            }
        }

        public void OnMouseMove(MouseMoveEventArgs e)
        {
            if (!e.Handled)
            {
                e.Handled = true;
                DetermineKey(e.ScreenToClient(e.Position));
            }
        }

        public void OnGotFocus()
        {
        }

        public void OnLostFocus()
        {
        }

        public bool IsFocused
        {
            get { return false; }
        }

        #endregion
        #region IScrollEventBehavior Members

        public void OnScroll(ScrollEventArgs e)
        {
            e.Handled = true;
            e.Cancel = true;
        }

        public void OnBeginScrollData(BeginScrollEventArgs e)
        {
            e.Handled = true;
            e.Cancel = true;
        }

        public void OnEndScrollData(EndScrollEventArgs e)
        {
            e.Handled = true;
        }

        #endregion
    }

    #region InternalToolTip
    class InternalToolTip : Label
    {
        private BackgroundVisual background;
        public InternalToolTip()
        {
            background = new BackgroundVisual { Texture = SysImages.MenuBackground, Colors = ColorUtil.SingleColor(ColorUtil.AlphaColor(Color.White, 128)) };
        }

        protected override void OnLayout()
        {
            background.Size = Size;
            base.OnLayout();
        }


        private IEnumerable<Visual> GetBaseVisuals() { return base.Visuals; }

        public override IEnumerable<Visual> Visuals
        {
            get
            {
                yield return background;
                foreach (var v in GetBaseVisuals()) yield return v;
            }
        }
    }
    #endregion
}
